# 版本: 1.0.0
# 作者: 陈振玺
# 功能: 邮件发送核心模块，支持附件、抄送、密送等功能

import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from email.utils import formataddr
from pathlib import Path
from typing import List, Optional, Union, Dict, Any
import mimetypes
from .config import EmailConfig
from .utils import FileHandler, ProgressBar


class EmailSender:
    """邮件发送器"""
    
    def __init__(self, config: EmailConfig):
        """初始化邮件发送器
        
        Args:
            config: 邮件配置对象
        """
        self.config = config
        self.file_handler = FileHandler()
    
    def send_email(self,
                   to_addresses: Union[str, List[str]],
                   subject: str,
                   content: str,
                   cc_addresses: Optional[Union[str, List[str]]] = None,
                   bcc_addresses: Optional[Union[str, List[str]]] = None,
                   attachments: Optional[List[Union[str, Path]]] = None,
                   content_type: str = "plain",
                   from_name: Optional[str] = None) -> Dict[str, Any]:
        """发送邮件
        
        Args:
            to_addresses: 收件人地址（字符串或列表）
            subject: 邮件主题
            content: 邮件内容
            cc_addresses: 抄送地址（可选）
            bcc_addresses: 密送地址（可选）
            attachments: 附件路径列表（可选）
            content_type: 内容类型 ("plain" 或 "html")
            from_name: 发件人显示名称（可选）
            
        Returns:
            发送结果字典
            
        Raises:
            Exception: 发送失败时抛出异常
        """
        try:
            # 标准化地址列表
            to_list = self._normalize_addresses(to_addresses)
            cc_list = self._normalize_addresses(cc_addresses) if cc_addresses else []
            bcc_list = self._normalize_addresses(bcc_addresses) if bcc_addresses else []
            
            # 创建邮件消息
            msg = self._create_message(
                to_list, cc_list, subject, content, content_type, from_name
            )
            
            # 处理附件
            attachment_info = []
            if attachments:
                attachment_info = self._process_attachments(msg, attachments)
            
            # 发送邮件
            all_recipients = to_list + cc_list + bcc_list
            self._send_message(msg, all_recipients)
            
            return {
                "success": True,
                "message": "邮件发送成功",
                "recipients": {
                    "to": to_list,
                    "cc": cc_list,
                    "bcc": bcc_list
                },
                "attachments": attachment_info,
                "subject": subject
            }
            
        except Exception as e:
            return {
                "success": False,
                "message": f"邮件发送失败: {str(e)}",
                "error": str(e)
            }
    
    def _normalize_addresses(self, addresses: Union[str, List[str]]) -> List[str]:
        """标准化邮件地址列表
        
        Args:
            addresses: 邮件地址（字符串或列表）
            
        Returns:
            标准化的地址列表
        """
        if isinstance(addresses, str):
            # 支持逗号分隔的多个地址
            return [addr.strip() for addr in addresses.split(',') if addr.strip()]
        elif isinstance(addresses, list):
            return [addr.strip() for addr in addresses if addr.strip()]
        else:
            return []
    
    def _create_message(self,
                       to_list: List[str],
                       cc_list: List[str],
                       subject: str,
                       content: str,
                       content_type: str,
                       from_name: Optional[str]) -> MIMEMultipart:
        """创建邮件消息对象
        
        Args:
            to_list: 收件人列表
            cc_list: 抄送列表
            subject: 邮件主题
            content: 邮件内容
            content_type: 内容类型
            from_name: 发件人显示名称
            
        Returns:
            邮件消息对象
        """
        msg = MIMEMultipart()
        
        # 设置发件人
        if from_name:
            msg['From'] = formataddr((from_name, self.config.username))
        else:
            msg['From'] = self.config.username
        
        # 设置收件人
        msg['To'] = ', '.join(to_list)
        
        # 设置抄送
        if cc_list:
            msg['Cc'] = ', '.join(cc_list)
        
        # 设置主题
        msg['Subject'] = subject
        
        # 添加邮件内容
        text_part = MIMEText(content, content_type, 'utf-8')
        msg.attach(text_part)
        
        return msg
    
    def _process_attachments(self, 
                           msg: MIMEMultipart, 
                           attachments: List[Union[str, Path]]) -> List[Dict[str, Any]]:
        """处理附件
        
        Args:
            msg: 邮件消息对象
            attachments: 附件路径列表
            
        Returns:
            附件信息列表
        """
        attachment_info = []
        
        for attachment_path in attachments:
            path = Path(attachment_path)
            
            if not path.exists():
                raise FileNotFoundError(f"附件文件不存在: {path}")
            
            if path.is_dir():
                # 处理文件夹附件（打包为ZIP）
                zip_path = self.file_handler.create_zip_archive(
                    path, 
                    show_progress=True
                )
                attachment_info.extend(
                    self._attach_file(msg, zip_path)
                )
            else:
                # 处理单个文件附件
                attachment_info.extend(
                    self._attach_file(msg, path)
                )
        
        return attachment_info
    
    def _attach_file(self, msg: MIMEMultipart, file_path: Path) -> List[Dict[str, Any]]:
        """附加单个文件到邮件
        
        Args:
            msg: 邮件消息对象
            file_path: 文件路径
            
        Returns:
            附件信息列表
        """
        file_size = file_path.stat().st_size
        
        # 检查文件大小限制（默认25MB）
        max_size = 25 * 1024 * 1024  # 25MB
        if file_size > max_size:
            raise ValueError(f"附件文件过大: {file_path.name} ({file_size / 1024 / 1024:.1f}MB > 25MB)")
        
        # 获取MIME类型
        mime_type, _ = mimetypes.guess_type(str(file_path))
        if mime_type is None:
            mime_type = 'application/octet-stream'
        
        main_type, sub_type = mime_type.split('/', 1)
        
        # 创建附件对象
        with open(file_path, 'rb') as f:
            if file_size > 1024 * 1024:  # 大于1MB显示进度
                progress_bar = ProgressBar(f"上传 {file_path.name}", file_size)
                
                attachment = MIMEBase(main_type, sub_type)
                data = b''
                chunk_size = 8192
                
                while True:
                    chunk = f.read(chunk_size)
                    if not chunk:
                        break
                    data += chunk
                    progress_bar.update(len(chunk))
                
                progress_bar.close()
                attachment.set_payload(data)
            else:
                attachment = MIMEBase(main_type, sub_type)
                attachment.set_payload(f.read())
        
        # 编码附件
        encoders.encode_base64(attachment)
        
        # 设置附件头信息
        attachment.add_header(
            'Content-Disposition',
            f'attachment; filename="{file_path.name}"'
        )
        
        # 添加到邮件
        msg.attach(attachment)
        
        return [{
            "name": file_path.name,
            "size": file_size,
            "type": mime_type,
            "path": str(file_path)
        }]
    
    def _send_message(self, msg: MIMEMultipart, recipients: List[str]) -> None:
        """发送邮件消息
        
        Args:
            msg: 邮件消息对象
            recipients: 所有收件人列表
            
        Raises:
            Exception: 发送失败时抛出异常
        """
        try:
            # 创建SMTP连接
            if self.config.use_ssl:
                # 使用SSL连接
                context = ssl.create_default_context()
                server = smtplib.SMTP_SSL(
                    self.config.smtp_server, 
                    self.config.smtp_port, 
                    context=context
                )
            else:
                # 使用普通连接
                server = smtplib.SMTP(self.config.smtp_server, self.config.smtp_port)
                
                if self.config.use_tls:
                    # 启用TLS
                    context = ssl.create_default_context()
                    server.starttls(context=context)
            
            # 登录
            if self.config.password:
                server.login(self.config.username, self.config.password)
            
            # 发送邮件
            text = msg.as_string()
            server.sendmail(self.config.username, recipients, text)
            
            # 关闭连接
            server.quit()
            
        except smtplib.SMTPAuthenticationError as e:
            raise Exception(f"SMTP认证失败: {e}")
        except smtplib.SMTPRecipientsRefused as e:
            raise Exception(f"收件人地址被拒绝: {e}")
        except smtplib.SMTPSenderRefused as e:
            raise Exception(f"发件人地址被拒绝: {e}")
        except smtplib.SMTPDataError as e:
            raise Exception(f"SMTP数据错误: {e}")
        except smtplib.SMTPException as e:
            raise Exception(f"SMTP错误: {e}")
        except Exception as e:
            raise Exception(f"邮件发送失败: {e}")
    
    def test_connection(self) -> Dict[str, Any]:
        """测试SMTP连接
        
        Returns:
            测试结果字典
        """
        try:
            if self.config.use_ssl:
                context = ssl.create_default_context()
                server = smtplib.SMTP_SSL(
                    self.config.smtp_server, 
                    self.config.smtp_port, 
                    context=context
                )
            else:
                server = smtplib.SMTP(self.config.smtp_server, self.config.smtp_port)
                
                if self.config.use_tls:
                    context = ssl.create_default_context()
                    server.starttls(context=context)
            
            # 测试登录
            if self.config.password:
                server.login(self.config.username, self.config.password)
            
            server.quit()
            
            return {
                "success": True,
                "message": "SMTP连接测试成功"
            }
            
        except Exception as e:
            return {
                "success": False,
                "message": f"SMTP连接测试失败: {e}",
                "error": str(e)
            }
    
    def validate_email_address(self, email: str) -> bool:
        """验证邮件地址格式
        
        Args:
            email: 邮件地址
            
        Returns:
            是否为有效的邮件地址
        """
        import re
        
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        return bool(re.match(pattern, email.strip()))
    
    def get_server_info(self) -> Dict[str, Any]:
        """获取SMTP服务器信息
        
        Returns:
            服务器信息字典
        """
        return {
            "smtp_server": self.config.smtp_server,
            "smtp_port": self.config.smtp_port,
            "username": self.config.username,
            "use_tls": self.config.use_tls,
            "use_ssl": self.config.use_ssl
        }